#ifndef V_CPU_H
#define V_CPU_H

#include "type.h"
#include "crregs.h"
#include "lazy_flags.h"
#ifdef DEBUG
#include <stdio.h>
#endif

#define CPL segment_regs[SREG_CS].val.rpl

#define AVM_READ 0
#define AVM_WRITE 1
#define AVM_EXECUTE 2
#define AVM_RW 3

// Number of TLB entries
#define TLB_SIZE 1024
#define TLB_SIZE_MASK ((TLB_SIZE - 1) << 12)
#define LPF_MASK 0xFFFFF000

#define TLB_INDEX_OF(lpf, len) (((lpf + len) & TLB_SIZE_MASK) >> 12)

#define AVM_READ_8BIT_REG(index) ((index & 4) ? \
		general_regs[index - 4].word.byte.rh : general_regs[index].word.byte.rl)
#define AVM_READ_16BIT_REG(index) (general_regs[index].word.rx)
#define AVM_READ_32BIT_REG(index) (general_regs[index].dword.erx)

#define AVM_WRITE_8BIT_REG(index, val) ((index & 4) ? \
		general_regs[index - 4].word.byte.rh = (bit8u)val :\
		general_regs[index].word.byte.rl = (bit8u)val)

#define AVM_WRITE_16BIT_REG(index, val) \
		general_regs[index].word.rx = (bit16u)rx;

#define AVM_WRITE_32BIT_REG(index, val) \
		general_regs[index].word.erx = (bit32u)erx;

inline unsigned AlignedAccessLPFOF(bit32u laddr, unsigned alignment) {
	return (laddr & (LPF_MASK | alignment));
}
#define TLB_LPFOF(laddr) AlignedAccessLPFOF(laddr, 0x7FF)

inline unsigned LPFOF(unsigned laddr) {
	return laddr & LPF_MASK;
}
#define PAGE_OFFSET(offset) (offset & 0xFFF)

struct avm_tlb_entry {
	avm_addr lpf;
	avm_phy_addr ppf;
	avm_hostpage_addr host_page_addr;
	bit32u access_bits;
};

struct tlb {
	avm_tlb_entry entry[TLB_SIZE];
};

const unsigned int MAX_GPRS = 8;
const unsigned int MAX_SREGS = 7; // Last for NIL

const unsigned int GPR_EAX = 0;
const unsigned int GPR_ECX = 1;
const unsigned int GPR_EDX = 2;
const unsigned int GPR_EBX = 3;
const unsigned int GPR_ESP = 4;
const unsigned int GPR_EBP = 5;
const unsigned int GPR_ESI = 6;
const unsigned int GPR_EDI = 7;
const unsigned int GPR_EIP = 8;
const unsigned int GPR_NIL = 9;
const unsigned int GPR_TMP = 10;

const unsigned int GPR_AX = 0;
const unsigned int GPR_CX = 1;
const unsigned int GPR_DX = 2;
const unsigned int GPR_BX = 3;
const unsigned int GPR_SP = 4;
const unsigned int GPR_BP = 5;
const unsigned int GPR_SI = 6;
const unsigned int GPR_DI = 7;

const unsigned int SREG_ES = 0;
const unsigned int SREG_CS = 1;
const unsigned int SREG_SS = 2;
const unsigned int SREG_DS = 3;
const unsigned int SREG_FS = 4;
const unsigned int SREG_GS = 5;
const unsigned int SREG_NIL = 6;

const unsigned int SSE_PREFIX_NONE = 0;
const unsigned int SSE_PREFIX_66 = 1;
const unsigned int SSE_PREFIX_F2 = 2;
const unsigned int SSE_PREFIX_F3 = 3;

const unsigned int AVM_IMMEDIATE_Ib = 2;

struct selector_t {
	bit8u rpl;
	bit8u ti;
	bit16u index;
};

struct descriptor_t {
#define ValidCache 0x1
#define SegAccessROK 0x02
#define SegAccessWOK 0x04

	unsigned valid;

	avm_bool p;
	bit8u dpl;
	avm_bool s;
	bit8u type;

	union {
		struct {
			avm_addr base;
			bit32u limit;

			avm_bool g;
			avm_bool d_b;
			avm_bool l;
			avm_bool avl;
		} segment_descriptor;
		// Other gates are not implemented yet.
	} u;
};

struct sreg_t {
	selector_t val; // Visiable part of segment register
	descriptor_t sreg_cache; // Hidden parg of segment register
};

struct gpr_t {
	union {
		struct {
			union {
				bit16u rx;
				struct {
					bit8u rl;
					bit8u rh;
				} byte;
			};
			bit16u word_filter;
		} word;

		struct {
			bit32u erx;
		} dword; 
	};
};

class v_cpu;
class instruction;

typedef void (v_cpu::*execute_ptr)(instruction *);
typedef avm_addr (v_cpu::*resolve_ptr)(instruction *);

#define AVM_CPU_CALL_METHOD(func, args) \
	(this->*((execute_ptr) (func))) args
#define AVM_CPU_CALL_METHODR(func, args) \
	(this->*((resolve_ptr) (func))) args

class instruction {
public:
	instruction() {};
	resolve_ptr resolve_modrm;

	void set_as32(bit8u bit) {
		meta_info.meta1 = (meta_info.meta1 & ~(1 << 4)) | (bit << 4);
	};
	bit8u as32() const {
		return meta_info.meta1 && 0x10;
	};
	void set_os32(bit8u bit) {
		meta_info.meta1 = (meta_info.meta1 & ~(1 << 4)) | (bit << 3);
	};
	void set_repUsed(bit8u val) {
		meta_info.meta1 = (meta_info.meta1 & ~3) | val;
	};

	void setModRM(bit8u val) {
		meta_data[AVM_METADATA_MODRM] = val;
	};
	/*void setMod(bit8u val) {
		mod = (val >> 6) & 3;
	};*/

	void setNnn(bit8u val) {
		meta_data[AVM_METADATA_NNN] = val;
	};
	void setRm(bit8u val) {
		meta_data[AVM_METADATA_RM] = val;
	};
	void setSibScale(bit8u val) {
		meta_data[AVM_METADATA_SCALE] = val;
	};
	void setSibIndex(bit8u val) {
		meta_data[AVM_METADATA_INDEX] = val;
	};
	void setSibBase(bit8u val) {
		meta_data[AVM_METADATA_BASE] = val;
	};
	
	void setDispl16s(bit16u val) {
		modrm_form.disp16 = (bit16s)val;
	};

	void setDispl32s(bit32u val) {
		modrm_form.disp32 = (bit32s)val;
	};

	bit8u sibBase() {
		return meta_data[AVM_METADATA_BASE];
	};

	bit8u sibIndex() {
		return meta_data[AVM_METADATA_INDEX];
	};

	bit8u sibScale() {
		return meta_data[AVM_METADATA_SCALE];
	};

	bit16s displ16s() {
		return (bit16s)(modrm_form.disp16);
	}
	
	bit32s displ32s() {
		return (bit32s)(modrm_form.disp32);
	}

	bit8u Ib() {
		return modrm_form.Ib;
	}
	bit16u Iw() {
		return modrm_form.Iw;
	}
	bit32u Id() {
		return modrm_form.Id;
	}

public:
	bit8u prefix;
	bit8u sib;
	
	execute_ptr execute1;
	execute_ptr execute2;
	// Metadata index
	static const bit8u AVM_METADATA_SEG = 0;
	static const bit8u AVM_METADATA_DEST = 1;
	static const bit8u AVM_METADATA_NNN = 2;
	static const bit8u AVM_METADATA_RM = 3;
	static const bit8u AVM_METADATA_SCALE = 4;
	static const bit8u AVM_METADATA_INDEX = 5;
	static const bit8u AVM_METADATA_BASE = 6;
	static const bit8u AVM_METADATA_MODRM = 7;
	bit8u meta_data[8];

	struct {
		union {
			bit8u Ib;
			bit16u Iw;
			bit32u Id;
		};
		union {
			bit16u disp16;
			bit32u disp32;
		};
	} modrm_form;
	struct {
		// 4...4 as32
		// 3...3 os32
		// 2...2 mod==0xC0
		// 1...0 repUsed (0=none, 1=0xF2, 2=0xF3)
		bit8u meta1;
	}meta_info;
};

// 8-bit registers
#define AL (general_regs[0].word.byte.rl)
#define CL (general_regs[1].word.byte.rl)
#define DL (general_regs[2].word.byte.rl)
#define BL (general_regs[3].word.byte.rl)
#define AH (general_regs[0].word.byte.rh)
#define CH (general_regs[1].word.byte.rh)
#define DH (general_regs[2].word.byte.rh)
#define BH (general_regs[3].word.byte.rh)

// 16-bit registers
#define AX (general_regs[0].word.rx)

// 32-bit registers
#define EAX (general_regs[0].dword.erx)

#define DECLARE_EFLAGS_ACCESSOR(name, bitnum) \
	bit32u get_##name(); \
	avm_bool getB_##name(); \
	void assert_##name(); \
	void clear_##name(); \
	void set_##name(avm_bool val);

#define IMPLEMENT_EFLAG_ACCESSOR(name, bitnum) \
	bit32u v_cpu::get_##name() { \
		return eflags & (1 << bitnum); \
	} \
	avm_bool getB_##name() { \
		return (eflags >> bitnum) & 1; \
	} 

#define IMPLEMENT_SET_EFLAGS_ACCESSOR(name, bitnum) \
	void v_cpu::assert_##name() { \
		eflags |= (1 << bitnum); \
	} \
	void v_cpu::clear_##name() { \
		eflag &= ~(1 << bitnum); \
	} \
	void v_cpu::set_##name(avm_bool val) { \
		eflags = (eflags & ~(1 << bitnum)) | ((val) << bitnum); \
	}

#define IMPLEMENT_SET_EFLAGS_ACCESSOR_AC(bitnum)

#define DECLARE_EFLAG_ACCESSOR_IOPL(bitnum) \
	void set_IOPL(bit32u val); \
	bit32u get_IOPL(); 

#define IMPLEMENT_SET_EFLAGS_ACCESSOR_IOPL(bitnum) \
	void v_cpu::set_IOPL(bit32u val) { \
		eflags = (eflags & ~(0x3 << bitnum)) | ((0x03 & val) << bitnum); \
	} \
	bit32u v_cpu::get_IOPL() { \
		return 3 & (eflags >> bitnum); \
	}

// Status flag
#define EFLAGS_CF   (1 <<  0)
#define EFLAGS_PF   (1 <<  2)
#define EFLAGS_AF   (1 <<  4)
#define EFLAGS_ZF   (1 <<  6)
#define EFLAGS_SF   (1 <<  7)
#define EFLAGS_OF   (1 << 11)
// Control flag
#define EFLAGS_DF   (1 << 10)
// System flag
#define EFLAGS_TF   (1 <<  8) // Unimplement yet!!!
#define EFLAGS_IF   (1 <<  9) // Unimplement yet!!!
#define EFLAGS_IOPL (1 << 12)
#define EFLAGS_NF   (1 << 14)
#define EFLAGS_RF   (1 << 16) // Unimplement yet!!!
#define EFLAGS_VM   (1 << 17) // Unimplement yet!!!
#define EFLAGS_AC   (1 << 18) // Unimplement yet!!!
#define EFLAGS_VIF  (1 << 19)
#define EFLAGS_VIP  (1 << 20)
#define EFLAGS_ID   (1 << 21)

#define EFLAGS_VALID_MASK 0x3F7FD5

#define EFLAGS_OSZAPC_MASK \
	EFLAGS_OF | EFLAGS_SF | EFLAGS_ZF | EFLAGS_AF | EFLAGS_PF | EFLAGS_CF;

#define EFLAGS_OSZAP_MASK \
	EFLAGS_OF | EFLAGS_SF | EFLAGS_ZF | EFLAGS_AF | EFLAGS_PF;

class v_cpu {
public:
	v_cpu();
	~v_cpu() {};
	
	void get_snapshot() const {};
	int fetch_decode(const bit8u * iptr, instruction *i, bit8u remaining_in_page);
	void test_instr(instruction *pi) {
		execute_ptr ec = pi->execute1;
		(this->*ec)(pi);
	}
	bit8u read_RMW_virtual_byte_32(unsigned seg, bit32u offset);
	void write_RMW_virtual_byte_32(unsigned seg, bit32u offset);

	void setCR3(unsigned val) {
		cr3 = val;	
	}
	unsigned getCR3() {
		return cr3;
	}
	unsigned getCR3_masked() {
		return cr3 & 0xFFFFF000;
	}

	void load_Eb(instruction *) {};
	void load_Ew(instruction *) {};
	void load_Ed(instruction *) {};

	// Instructions
	void aaa(instruction *);
	void aad(instruction *)	{};
	void aam(instruction *) {};
	void aas(instruction *) {};
	
	void adc_ALIb(instruction *) {};
	void adc_AXIw(instruction *) {};
	void adc_EAXId(instruction *) {};
	void adc_EbGbM(instruction *) {};
	void adc_EwGwM(instruction *) {};
	void adc_EdGdM(instruction *) {};
	void adc_EbIbR(instruction *) {};
	void adc_EwIwR(instruction *) {};
	void adc_EdIdR(instruction *) {};
	void adc_EbIbM(instruction *) {};
	void adc_EwIwM(instruction *) {};
	void adc_EdIdM(instruction *) {};
	void adc_EbGbR(instruction *) {};
	void adc_EwGwR(instruction *) {};
	void adc_EdGdR(instruction *) {};
	void adc_GbEbR(instruction *) {};
	void adc_GwEwR(instruction *) {};
	void adc_GdEdR(instruction *) {};
	void adc_GbEbM(instruction *) {};
	void adc_GwEwM(instruction *) {};
	void adc_GdEdM(instruction *) {};

	void add_ALIb(instruction *) {};
	void add_AXIw(instruction *) {};
	void add_EAXId(instruction *) {};
	void add_EbGbM(instruction *);
	void add_EwGwM(instruction *) {};
	void add_EdGdM(instruction *) {};
	void add_EbIbR(instruction *) {};
	void add_EwIwR(instruction *) {};
	void add_EdIdR(instruction *) {};
	void add_EbIbM(instruction *) {};
	void add_EwIwM(instruction *) {};
	void add_EdIdM(instruction *) {};
	void add_EbGbR(instruction *);
	void add_EwGwR(instruction *);
	void add_EdGdR(instruction *) {};
	void add_GbEbR(instruction *) {};
	void add_GwEwR(instruction *) {};
	void add_GdEdR(instruction *) {};
	void add_GbEbM(instruction *) {};
	void add_GwEwM(instruction *) {};
	void add_GdEdM(instruction *) {};
//private:
	// RIP
	// NIL
	// TMP
	gpr_t general_regs[MAX_GPRS + 3];
	sreg_t segment_regs[MAX_SREGS];
	avm_cr0_t cr0;
	unsigned cr3;
	unsigned cr3_masked;
	tlb TLB;

	struct {
		avm_phy_addr paddr1;
		avm_phy_addr paddr2;
		bit32u len1;
		bit32u len2;
		bit32u pages;
	} address_xlate;

	bit32u eflags;
	bit32u lf_flags_status;
	avm_lf_flags_entry oszapc;
#define ArithmeticalFlag(flag, lfMask, eflagsBitShift) \
	avm_bool get_##flag##Lazy(); \
	avm_bool getB_##flag(void) { \
		if ((lf_flags_status & lfMask) == 0) { \
			return (eflags >> eflagsBitShift) & 1; \
		} \
		else { \
			return !!get_##flag##Lazy(); \
		} \
	} \
	avm_bool get_##flag(void) { \
		if ((lf_flags_status & lfMask) == 0) { \
			return (eflags & lfMask); \
		} \
		else { \
			get_##flag##Lazy(); \
		} \
	} \
	void set_##flag(avm_bool val) { \
		lf_flags_status &= ~lfMask; \
		eflags &= ~(lfMask); \
		eflags |= (val << eflagsBitShift); \
	} \
	void clear_##flag() { \
		lf_flags_status &= ~lfMask; \
		eflags &= ~lfMask; \
	} \
	void assert_##flag() { \
		lf_flags_status &= ~lfMask; \
		eflags |= lfMask; \
	} \
	void force_##flag() { \
		if ((lf_flags_status & lfMask) != 0) { \
			set_##flag(!!get_##flag##Lazy()); \
		} \
	}
	ArithmeticalFlag(CF, EflagsCFMask, 0);
	ArithmeticalFlag(PF, EflagsPFMask, 2);
	ArithmeticalFlag(AF, EflagsAFMask, 4);
	ArithmeticalFlag(ZF, EflagsZFMask, 6);
	ArithmeticalFlag(SF, EflagsSFMask, 7);
	ArithmeticalFlag(OF, EflagsOFMask, 11);

public:
	bit32u get_laddr(bit8u seg, bit32u offset);
	// Convert guest os linear address to guest os physical address
	avm_phy_addr translate_linear(bit32u laddr, unsigned cpl, unsigned xlate_rw);

	// Interfaces for guest OS accessing linear address
	void access_read_linear(bit32u laddr, unsigned len, unsigned cpl, 
			unsigned xlate_rw, void *data);	
	void access_write_linear(bit32u laddr, unsigned len, unsigned cpl,
			unsigned xlate_rw, void *data);

	// Interfaces for vcpu accessing its physical address
	void access_read_physical(avm_phy_addr paddr, unsigned len, void *data);
	void access_write_physical(avm_phy_addr paddr, unsigned len ,void *data);	

	void initialize() {
		AL = 0;
	};
	void TLB_init();
	void TLB_flush();

	bit8u avm_read_8bit_reg(bit8u index) const {
		return (index & 4) ?general_regs[index - 4].word.byte.rh : 
			general_regs[index].word.byte.rl;
	}
	bit16u avm_read_16bit_reg(bit8u index) const {
		return general_regs[index].word.rx;
	}
	bit32u avm_read_32bit_reg(bit8u index) const {
		return general_regs[index].dword.erx;
	}
	void avm_write_8bit_reg(bit8u index, bit8u val) {
		if (index & 4) {
			general_regs[index - 4].word.byte.rh = val;
		}
		else {
			general_regs[index].word.byte.rl = val;
		}
	}
	void avm_write_16bit_reg(bit8u index, bit16u val) {
		general_regs[index].word.rx = val;
	}
	void avm_write_32bit_reg(bit8u index, bit32u val) {
		general_regs[index].dword.erx = val;	
	}
	

	bit8u fetch_byte(const bit8u *p) {
		return p[0];
	}
	bit16u fetch_word(const bit8u *p) {
		return (p[0] | (p[1] << 8));
	}
	bit32u fetch_dword(const bit8u *p) {
		bit32u data;
		data = (p[0] | (p[1] << 8) | (p[2] << 16) | (p[3] << 24));
		return data;
	}
	avm_addr resolve16_base_index(instruction *);
	avm_addr resolve32_base(instruction *);
	avm_addr resolve32_base_index(instruction *);
};

#define SET_EFLAGS_OSZAPC_SIZE(size, op1, op2, result, instr) \
	oszapc.op1 = (avm_address)(bit##size##s)op1; \
	oszapc.op2 = (avm_address)(bit##size##s)op2; \
	oszapc.result = (avm_address)(bit##size##s)result; \
	oszapc.instr = instr; \
	lf_flags_status = EFLAGS_OSZAPC_MASK;

#define SET_EFLAGS_OSZAPC_8(op1, op2, result, instr) \
	SET_EFLAGS_OSZAPC_SIZE(8, op1, op2, result, instr)
#define SET_EFLAGS_OSZAPC_16(op1, op2, result, instr) \
	SET_EFLAGS_OSZAPC_SIZE(16, op1, op2, result, instr)
#define SET_EFLAGS_OSZAPC_32(op1, op2, result, instr) \
	SET_EFLAGS_OSZAPC_SIZE(32, op1, op2, result, instr)

#define SET_EFLAGS_OSZAPC_ADD_8(op1, op2, result) \
	SET_EFLAGS_OSZAPC_8(op1, op2, result, AVM_LF_INSTR_ADD8)

#define SET_EFLAGS_OSZAPC_ADD_16(op1, op2, result) \
	SET_EFLAGS_OSZAPC_16(op1, op2, result, AVM_LF_INSTR_ADD8)


#define SET_EFLAGS_OSZAP_SIZE(size, op1, op2, result, instr) \
	force_CF(); \
	oszapc.op1 = (avm_address)(bit##size##s)op1; \
	oszapc.op1 = (avm_address)(bit##size##s)op1; \
	oszapc.result = (avm_address)(bit##size##s)result; \
	oszapc.instr = instr; \
	lf_flags_status = EFLAGS_OSZAP_MASK;

#endif
